<!--
 * @Description: 发布页面
 * @Author: Kerwin
 * @Date: 2023-06-29 17:12:32
 * @LastEditTime: 2024-11-24 17:09:27
 * @LastEditors:  Please set LastEditors
-->
<template>
  <u-navbar back-icon-name="close" back-icon-size="30rpx" backIconColor="#000" :borderBottom="false">
    <template v-slot>
      <view style="width: 300rpx;margin: 0 auto;">
        <u-tabs bg-color="transparent" active-color="#000" inactive-color="#666" :bar-style="{ 'background': '#FF6C18' }"
          :list="tabs" :is-scroll="false" v-model="current" @change="tabChange"></u-tabs>
      </view>
    </template>
  </u-navbar>
  <view class="container">
    <view class="section">
      <!-- 输入框 -->
      <view ref="textRef" id="editor" class="textarea" spellcheck="false" tabindex="0" contenteditable
        data-placeholder="输入内容描述…" @blur="onBlur">
      </view>
      <!-- 上传组件 -->
      <view class="upload" contenteditable="false">
        <c_upload :customBtn="true" width="176" height="176" :max-count="9" @on-uploaded="uploadUploaded">
          <template v-slot:addBtn>
            <view class="slot-btn" hover-class="slot-btn__hover" hover-stay-time="150">
              <u-icon name="plus" size="50" color="#E1E3E5"></u-icon>
            </view>
          </template>
        </c_upload>
      </view>
      <!-- 标签/关键词 -->
      <view class="keywords">
        <view class="keywords-btn" @click="toAddKeywords">
          <u-icon :name="keywordsIcon" color="#000" size="36"></u-icon>
          <view class="keywords-text">{{ currentTab.tagsLabel }}</view>
        </view>
        <c_tagSelector ref="tagsRef" @onConfirm="doKeywordsAdd" />
        <view class="keywords-list">
          <view @click="doKeywordsAdd(item)" v-for="(item, index) in currentTab.defualtTags" :key="index"
            class="keywords-item">
            {{ item.name }}</view>
        </view>
      </view>
    </view>
    <view style="height: 20rpx;width: 100%;background: #F7F7F7;"></view>
    <view class="section range">
      <view v-for="item in postRanges" :key="item.type" class="post-range" :class="{ active: item.active }">
        {{ item.label }}
      </view>
    </view>
    <view class="actions">
      <u-button class="submitBtn btn2" @click="submit(enumAll.audit_status_enum.SUCCESS)" type="primary"
        ripple>发布</u-button>
      <u-button class="submitBtn btn1" size="medium" plain @click="submit(enumAll.audit_status_enum.DEFAULT)"
        ripple>存草稿</u-button>
    </view>
  </view>
</template>
<script setup lang="ts">
import { reactive, ref, shallowRef, nextTick, computed } from 'vue';
import { onLoad, onShow, onReady } from '@dcloudio/uni-app'
import { storeToRefs } from 'pinia'
import { baseApi, productApi, socialApi, enumAll } from '@/api'
import c_upload from './custom-upload.vue'
import c_tagSelector from './c_tagSelector.vue';
import { getImgFullPath, getDistance } from '@/utils/index'
import { useUserStore } from '@/store'
import keywordsIcon from '../../static/img/keyword.svg'

const userStore = useUserStore()
const { hasLogin, hasNewDynamic } = storeToRefs(userStore)

const tabs = reactive([{
  name: '动态',
  type: 3,
  tagsLabel: '添加关键词',
  tagType: '',
  defualtTags: [{
    name: '黑银',
    id: 1
  }, {
    name: '人脉',
    id: 3
  },
  {
    name: '营销推广',
    id: 3
  },
  {
    name: '瑜伽',
    id: 3
  }]
}, {
  name: '需求',
  type: 4,
  tagsLabel: '选择分类',
  tagType: 5,
  defualtTags: [{
    name: '互联网',
    id: 1
  }, {
    name: '实体店',
    id: 3
  },
  {
    name: '地推',
    id: 3
  },
  {
    name: '培训',
    id: 3
  }]
},{
  name: '服务',
  type: 5,
  tagsLabel: '选择分类',
  tagType: 5,
  defualtTags: [{
    name: '互联网',
    id: 1
  }, {
    name: '实体店',
    id: 3
  },
  {
    name: '地推',
    id: 3
  },
  {
    name: '培训',
    id: 3
  }]
}])
const current = ref(0)
const defaultType = tabs[0].type
const tabChange = (index: any) => {
  current.value = index
  formData.type = tabs[index].type
}
const currentTab = computed(() => {
  return tabs[current.value]
})
enum Status {
  Default = 0,
  UnderReview = 10,
  Approved = 20,
  Rejected = 21,
}
enum ContentType {
  Image = 1,
  Audio = 2,
  Video = 3,
}
interface FormData {
  id?: string;
  content: string;
  contentType: ContentType;
  videoUrl: string;
  previewImage?: string;
  status: Status;
  tags?: string[];
  type: number;
  userId: number;
}

const formData: FormData = reactive({
  id: '',
  content: '',
  contentType: ContentType.Image,
  videoUrl: '',
  previewImage: '',
  status: Status.Default,
  tags: [],
  type: defaultType,
  userId: userStore.userInfo.id,
});

const postRanges = ref([{
  label: '发到圈子',
  type: 1,
  active: true
}
])
const textRef = ref()
const selection = shallowRef()
const range = shallowRef()
const onBlur = () => {
  selection.value = window.getSelection();
  range.value = selection.value?.getRangeAt(0);
  //如果最后的光标停留在text节点，那么就把光标移动至editor的最后面
  if (range.value.endContainer.nodeType === Node.TEXT_NODE) { // 检查结束节点是否为文本节点
    resetCursor()
  }
};

/**
 * 重置光标位置
 * */
const resetCursor = () => {
  const parentElement = document.getElementById('editor'); // 获取结束节点的父元素
  let ran = document.createRange();
  // @ts-ignore
  ran.selectNodeContents(parentElement);
  ran.collapse(false);
  let sel = window.getSelection();
  sel?.removeAllRanges();
  sel?.addRange(ran);
  // @ts-ignore
  range.value = sel.getRangeAt(0);
}

/**
 *  点击工具栏按钮添加文本节点
 */
const getValue = (value: string) => {
  // 创建一个文本节点
  const textNode = document.createTextNode(value);
  // 在光标位置插入文本节点
  range.value?.insertNode(textNode);
  // 移动光标到文本节点的末尾
  range.value?.setStartAfter(textNode);
  // 折叠光标到文本节点的末尾
  range.value?.collapse(true);
  // 移除所有选区 不移除selection会到聚焦点击的文本
  selection.value?.removeAllRanges();
  // 添加选区
  selection.value?.addRange(range.value);
};

/**
 *  点击参数生成span标签
 */
const getSpan = (params: { id: number; name: string }) => {
  // 创建前缀
  let prefix = `<span contenteditable="false" disabled="disabled" class="fn-param" data-param="${params.name}">`;
  // 创建后缀
  let suffix = "</span>";
  // 创建span元素
  let el = document.createElement("span");
  // 将前缀和后缀插入span元素
  el.innerHTML = prefix + '#' + params.name + suffix;
  // 去掉外层的span

  let frag = document.createDocumentFragment();
  // @ts-ignore
  let node = frag.appendChild(el.firstChild);

  // 插入tag
  range.value?.insertNode(node);
  // 设置光标
  range.value?.setStartAfter(node);
  range.value?.collapse(true);
  // 不移除selection会到聚焦点击的文本
  selection.value?.removeAllRanges();
  // 添加选区
  selection.value?.addRange(range.value);
};

/**
 * 获取构建的html里面的文本和参数
 */
// Type guard for Text nodes
function isTextNode(node: ChildNode): node is Text {
  return node.nodeType === Node.TEXT_NODE;
}
// Type guard for Span elements
function isSpanElement(node: ChildNode): node is HTMLSpanElement {
  return node.nodeType === Node.ELEMENT_NODE && node.nodeName === 'SPAN';
}
const getTextAndParams = () => {
  let editor = document.getElementById('editor') as HTMLDivElement;
  let result = '';
  let tags = []
  const childNodes = Array.from(editor.childNodes) as ChildNode[];
  for (const node of childNodes) {
    if (isTextNode(node)) {
      result += node.textContent || '';
    } else if (isSpanElement(node)) {
      result += node.dataset.param || '';
      node.dataset.param && tags.push(node.dataset.param)
    }
  }
  let data = {
    value: result,
    innerHTML: editor.innerHTML,
    tags: tags
  };
  return data;
};

const tagsRef = ref()
const toAddKeywords = () => {
  tagsRef.value.open(currentTab.value.tagType)
}



// 文件上传成功回调
function uploadUploaded(lists: any, name: string, contentType: ContentType) {
  console.log(lists, name, contentType)
  const urlString = lists.reduce((pre: any, cur: { response: { data: any; }; }) => {
    return `${pre ? pre + ',' : ''}${cur.response.data}`
  }, '')
  formData.videoUrl = urlString
  if (contentType) {
    // 组件内部只抛出了视频的内容类型
    formData.contentType = contentType
    formData.previewImage = `${urlString}?x-oss-process=video/snapshot,t_0,f_jpg,w_540,ar_auto`
  }
}

const doKeywordsAdd = (params: { id: number; name: string }) => {
  getSpan(params)
}

// 表单验证
const validateFn = () => {
  if (!formData.content || formData.content === '<!---->') {
    uni.showToast({
      title: '请填写内容',
      icon: 'none'
    })
    return false
  }
  if (!formData.videoUrl) {
    uni.showToast({
      title: '请上传视频或图片',
      icon: 'none'
    })
    return false
  }
  return true
}

/**
 * 发布动态
 * 
 * @param {any} status 审核状态
 * @returns 
 * 
 */

const dynamicAdd = async (status: number) => {
  const params = uni.$u.deepClone(formData)
  if (!params.tags.length) {
    delete params.tags
  } else {
    params.tags = JSON.stringify(params.tags)
  }
  uni.showLoading()
  await socialApi.dynamicAdd({
    ...params,
    status
  }).then((res: any) => {
    if (res.code === 200) {
      uni.showToast({
        title: `${status === enumAll.audit_status_enum.SUCCESS ? '发布' : '存草稿'}成功`,
      })
    }
  }).finally(() => {
    uni.hideLoading()
  })
}
const dynamicUpdate = async (status: any) => {
  const { data, code } = await socialApi.dynamicUpdate({
    ...formData,
    status
  })
  if (code === 200) {
    uni.showToast({
      title: '发布成功',
      success() {
        hasNewDynamic.value = true
        uni.navigateBack()
      }
    })
  }
}
const submit = async (status: any) => {
  const data = getTextAndParams()
  formData.content = data.innerHTML
  formData.tags = data.tags
  if (!validateFn()) return
  await dynamicAdd(status)
}

/**
 * 获取编辑器焦点
 */
function getEditableDivFocus() {
  const editorElement = document.getElementById('editor') as HTMLDivElement;
  editorElement.focus();
}
nextTick(() => {
  getEditableDivFocus()
})

onLoad((option) => {

})
</script>


<style lang="scss" scoped>
.container {
  background: #fff;
  height: calc(100vh - 80rpx);
  position: relative;

  .section {
    background: #fff;
    padding: 24rpx 24rpx 30rpx 24rpx;

    .textarea {
      height: 240rpx;
      overflow: hidden;
      box-sizing: border-box;
      position: relative;

      &:focus {
        outline: none;
      }

      &:empty::before {
        content: attr(data-placeholder);
        color: #CCCCCC;
      }

      :deep(.fn-param) {
        color: #1884FF;
        margin: 0 8rpx;
      }
    }

    .upload {
      margin-bottom: 20rpx;

      .slot-btn {
        display: block;
        width: 176rpx;
        height: 176rpx;
        border-radius: 16rpx;
        border: 2rpx dashed #E1E3E5;
        display: flex;
        justify-content: center;
        align-items: center;
      }
    }

    .keywords {

      .keywords-btn {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 220rpx;
        height: 64rpx;
        border-radius: 4px;
        background: #F7F7F7;
        margin-bottom: 24rpx;

        &:active {
          background: #E1E3E5;
        }

        .keywords-text {
          margin-left: 5rpx;

        }
      }

      .keywords-list {
        display: flex;
        flex-wrap: wrap;
        gap: 10rpx;

        .keywords-item {
          display: flex;
          flex-direction: column;
          padding: 6px 8px;
          box-sizing: border-box;
          border: 1rpx solid #E1E3E5;
          font-size: 28rpx;
          color: #666;
          border-radius: 4px;

          &:active {
            background: #E1E3E5;
          }
        }
      }
    }

    &.range {
      display: flex;
      gap: 20rpx;

      .post-range {
        display: flex;
        padding: 8px 12px;
        background: #F7F7F7;
        color: #666666;
        border-radius: 8rpx;

        &:active,
        &.active {
          color: #FF6C18;
          background: #FFF0E7;
        }
      }

    }

    .tips {
      font-size: 20rpx;
      color: red;
    }
  }
}

.actions {
  // display: flex;
  // justify-content: space-between;
  margin-top: 240rpx;
  // position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  padding: 0 20rpx calc(env(safe-area-inset-bottom) + 40rpx) 20rpx;

  .submitBtn {
    background: #FF6C18;
    margin: 0;
    border-radius: 16rpx;

    &.btn1 {
      width: 260rpx;
      background: #fff;
      margin: 0 auto;
      margin-top: 20rpx;
      display: block;
      border: none;
      outline: none;

      &:after {
        display: none;
      }
    }

    &.btn2 {
      flex: 1;
      // margin-left: 20rpx;
    }
  }
}
</style>
